/* parse EXIF metadata block out into a set of fields, and reassemble EXIF
 * block from original block, plus modified fields
 *
 * 7/11/16
 *      - from jpeg2vips
 * 14/10/17
 * 	- only read orientation from ifd0
 * 1/2/18
 * 	- remove exif thumbnail if "jpeg-thumbnail-data" has been removed
 * 3/7/18
 * 	- add support for writing string-valued fields
 * 9/7/18 [@Nan619]
 * 	- get tag name from tag plus ifd
 * 13/11/21
 * 	- better handling of strings with embedded metacharacters
 */

/*

	This file is part of VIPS.

	VIPS is free software; you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
	02110-1301  USA

 */

/*

	These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk

 */

/*
#define DEBUG_VERBOSE
#define DEBUG
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <glib/gi18n-lib.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include <vips/vips.h>
#include <vips/buf.h>
#include <vips/internal.h>
#include <vips/debug.h>

#ifdef HAVE_EXIF

#ifdef UNTAGGED_EXIF
#include <exif-data.h>
#include <exif-loader.h>
#include <exif-ifd.h>
#include <exif-utils.h>
#else /*!UNTAGGED_EXIF*/
#include <libexif/exif-data.h>
#include <libexif/exif-loader.h>
#include <libexif/exif-ifd.h>
#include <libexif/exif-utils.h>
#endif /*UNTAGGED_EXIF*/

#include "pforeign.h"

// render an entry as a utf8 string
static char *
entry_to_s(ExifEntry *entry)
{
	/* Some extra space for conversion to string ... this can be quite a bit
	 * for formats like float. Ban crazy size values.
	 */
	int size = VIPS_MIN(entry->size, 10000);
	int max_size = size * 3 + 32;
	char *text = VIPS_MALLOC(NULL, max_size + 1);

	// this renders floats as eg. "12.2345", enums as "Inch", etc.
	exif_entry_get_value(entry, text, max_size);

	// libexif does not null-terminate ASCII strings, we must add the \0
	// ourselves
	if (entry->format == EXIF_FORMAT_ASCII)
		text[size] = '\0';

	char *utf8 = g_utf8_make_valid(text, -1);

	g_free(text);

	return utf8;
}

#ifdef DEBUG_VERBOSE
/* Print exif for debugging ... hacked from exif-0.6.9/actions.c
 */
static void
show_tags(ExifData *data)
{
	int i;
	unsigned int tag;
	const char *name;

	printf("show EXIF tags:\n");

	for (i = 0; i < EXIF_IFD_COUNT; i++)
		printf("%-7.7s", exif_ifd_get_name(i));
	printf("\n");

	for (tag = 0; tag < 0xffff; tag++) {
		name = exif_tag_get_title(tag);
		if (!name)
			continue;
		printf("  0x%04x %-29.29s", tag, name);
		for (i = 0; i < EXIF_IFD_COUNT; i++)
			if (exif_content_get_entry(data->ifd[i], tag))
				printf("   *   ");
			else
				printf("   -   ");
		printf("\n");
	}
}

static void
show_entry(ExifEntry *entry, void *client)
{
	char *text = entry_to_s(entry);

	printf("%s", exif_tag_get_title(entry->tag));
	printf("|");
	printf("%s", text);
	printf("|");
	printf("%s", exif_format_get_name(entry->format));
	printf("|");
	printf("%d bytes", entry->size);
	printf("\n");

	g_free(text);
}

static void
show_ifd(ExifContent *content, void *client)
{
	int *ifd = (int *) client;

	printf("- ifd %d\n", *ifd);
	exif_content_foreach_entry(content, show_entry, client);

	*ifd += 1;
}

static void
show_values(ExifData *data)
{
	ExifByteOrder order;
	int ifd;

	order = exif_data_get_byte_order(data);
	printf("EXIF tags in '%s' byte order\n",
		exif_byte_order_get_name(order));

	printf("Title|Value|Format|Size\n");

	ifd = 0;
	exif_data_foreach_content(data, show_ifd, &ifd);

	if (data->size)
		printf("contains thumbnail of %d bytes\n", data->size);
}
#endif /*DEBUG_VERBOSE*/

/* Like exif_data_new_from_data(), but don't default missing fields.
 *
 * If we do exif_data_new_from_data(), then missing fields are set to
 * their default value and we won't know about it.
 */
static ExifData *
vips_exif_load_data_without_fix(const void *data, size_t length)
{
	ExifData *ed;

	/* exif_data_load_data() only allows uint for length. Limit it to less
	 * than that: 2**23 should be enough for anyone.
	 */
	if (length < 4) {
		vips_error("exif", "%s", _("exif too small"));
		return NULL;
	}
	if (length > 1 << 23) {
		vips_error("exif", "%s", _("exif too large"));
		return NULL;
	}

	if (!(ed = exif_data_new())) {
		vips_error("exif", "%s", _("unable to init exif"));
		return NULL;
	}

	exif_data_unset_option(ed, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION);
	if (!vips_isprefix("Exif", (char *) data)) {
		/* Ensure "Exif" prefix as loaders may not provide it.
		 */
		void *data_with_prefix;
		data_with_prefix = g_malloc0(length + 6);
		memcpy(data_with_prefix, "Exif\0\0", 6);
		memcpy((char *) data_with_prefix + 6, data, length);
		exif_data_load_data(ed, data_with_prefix, length + 6);
		g_free(data_with_prefix);
	}
	else
		exif_data_load_data(ed, data, length);

	return ed;
}

static int
vips_exif_get_int(ExifData *ed,
	ExifEntry *entry, unsigned long component, int *out)
{
	ExifByteOrder bo = exif_data_get_byte_order(ed);
	size_t sizeof_component = entry->size / entry->components;
	size_t offset = component * sizeof_component;

	if (entry->format == EXIF_FORMAT_SHORT)
		*out = exif_get_short(entry->data + offset, bo);
	else if (entry->format == EXIF_FORMAT_SSHORT)
		*out = exif_get_sshort(entry->data + offset, bo);
	else if (entry->format == EXIF_FORMAT_LONG)
		/* This won't work for huge values, but who cares.
		 */
		*out = (int) exif_get_long(entry->data + offset, bo);
	else if (entry->format == EXIF_FORMAT_SLONG)
		*out = exif_get_slong(entry->data + offset, bo);
	else
		return -1;

	return 0;
}

static int
vips_exif_get_rational(ExifData *ed,
	ExifEntry *entry, unsigned long component, ExifRational *out)
{
	if (entry->format == EXIF_FORMAT_RATIONAL) {
		ExifByteOrder bo = exif_data_get_byte_order(ed);
		size_t sizeof_component = entry->size / entry->components;
		size_t offset = component * sizeof_component;

		*out = exif_get_rational(entry->data + offset, bo);
	}
	else
		return -1;

	return 0;
}

static int
vips_exif_get_srational(ExifData *ed,
	ExifEntry *entry, unsigned long component, ExifSRational *out)
{
	if (entry->format == EXIF_FORMAT_SRATIONAL) {
		ExifByteOrder bo = exif_data_get_byte_order(ed);
		size_t sizeof_component = entry->size / entry->components;
		size_t offset = component * sizeof_component;

		*out = exif_get_srational(entry->data + offset, bo);
	}
	else
		return -1;

	return 0;
}

static int
vips_exif_get_double(ExifData *ed,
	ExifEntry *entry, unsigned long component, double *out)
{
	ExifRational rv;
	ExifSRational srv;
	double value;

	if (!vips_exif_get_rational(ed, entry, component, &rv)) {
		if (rv.denominator == 0)
			value = 0;
		else
			value = (double) rv.numerator / rv.denominator;
	}
	else if (!vips_exif_get_srational(ed, entry, component, &srv)) {
		if (srv.denominator == 0)
			value = 0;
		else
			value = (double) srv.numerator / srv.denominator;
	}
	else
		return -1;

	*out = value;

	return 0;
}

/* Save an exif value to a string in a way that we can restore. We only bother
 * for the simple formats (that a client might try to change) though.
 *
 * Keep in sync with vips_exif_from_s() below.
 */
static void
vips_exif_to_s(ExifData *ed, ExifEntry *entry, VipsDbuf *buf)
{
	char *text = entry_to_s(entry);

	unsigned long i;
	int iv;
	ExifRational rv;
	ExifSRational srv;

	if (entry->components < 10 &&
		!vips_exif_get_int(ed, entry, 0, &iv)) {
		for (i = 0; i < entry->components; i++) {
			vips_exif_get_int(ed, entry, i, &iv);
			vips_dbuf_writef(buf, "%d ", iv);
		}
	}
	else if (entry->components < 10 &&
		!vips_exif_get_rational(ed, entry, 0, &rv)) {
		for (i = 0; i < entry->components; i++) {
			vips_exif_get_rational(ed, entry, i, &rv);
			vips_dbuf_writef(buf, "%u/%u ", rv.numerator, rv.denominator);
		}
	}
	else if (entry->components < 10 &&
		!vips_exif_get_srational(ed, entry, 0, &srv)) {
		for (i = 0; i < entry->components; i++) {
			vips_exif_get_srational(ed, entry, i, &srv);
			vips_dbuf_writef(buf, "%d/%d ", srv.numerator, srv.denominator);
		}
	}
	else
		vips_dbuf_writef(buf, "%s ", text);

	vips_dbuf_writef(buf, "(%s, %s, %lu components, %d bytes)",
		text,
		exif_format_get_name(entry->format),
		entry->components,
		entry->size);

	g_free(text);
}

typedef struct _VipsExifParams {
	VipsImage *image;
	ExifData *ed;
} VipsExifParams;

/* tags do not uniquely set tag names: the same tag can have different
 * names in different ifds.
 *
 * As long as this entry has been linked to an ifd, get the tag name.
 */
static const char *
vips_exif_entry_get_name(ExifEntry *entry)
{
	if (!entry->parent)
		return NULL;

	return exif_tag_get_name_in_ifd(entry->tag,
		exif_entry_get_ifd(entry));
}

static void
vips_exif_attach_entry(ExifEntry *entry, VipsExifParams *params)
{
	const char *tag_name;
	char vips_name_txt[256];
	VipsBuf vips_name = VIPS_BUF_STATIC(vips_name_txt);
	VipsDbuf value = { 0 };

	if (!(tag_name = vips_exif_entry_get_name(entry)))
		return;

	vips_buf_appendf(&vips_name, "exif-ifd%d-%s",
		exif_entry_get_ifd(entry), tag_name);
	vips_exif_to_s(params->ed, entry, &value);

	/* Can't do anything sensible with the error return.
	 */
	(void) vips_image_set_string(params->image,
		vips_buf_all(&vips_name),
		(char *) vips_dbuf_string(&value, NULL));

	vips_dbuf_destroy(&value);
}

static void
vips_exif_get_content(ExifContent *content, VipsExifParams *params)
{
	exif_content_foreach_entry(content,
		(ExifContentForeachEntryFunc) vips_exif_attach_entry, params);
}

static int
vips_exif_entry_get_double(ExifData *ed, int ifd, ExifTag tag, double *out)
{
	ExifEntry *entry;

	if (!(entry = exif_content_get_entry(ed->ifd[ifd], tag)) ||
		entry->components != 1)
		return -1;

	return vips_exif_get_double(ed, entry, 0, out);
}

static int
vips_exif_entry_get_int(ExifData *ed, int ifd, ExifTag tag, int *out)
{
	ExifEntry *entry;

	if (!(entry = exif_content_get_entry(ed->ifd[ifd], tag)) ||
		entry->components != 1)
		return -1;

	return vips_exif_get_int(ed, entry, 0, out);
}

/* Set the image resolution from the EXIF tags.
 */
static int
vips_image_resolution_from_exif(VipsImage *image, ExifData *ed)
{
	double xres, yres;
	int unit;

	/* The main image xres/yres are in ifd0. ifd1 has xres/yres of the
	 * image thumbnail, if any.
	 *
	 * Don't warn about missing res fields, it's very common, especially for
	 * things like webp.
	 */
	if (vips_exif_entry_get_double(ed, 0, EXIF_TAG_X_RESOLUTION, &xres) ||
		vips_exif_entry_get_double(ed,
			0, EXIF_TAG_Y_RESOLUTION, &yres))
		return -1;

	/* resuint is optional and defaults to inch.
	 */
	unit = 2;
	(void) vips_exif_entry_get_int(ed,
		0, EXIF_TAG_RESOLUTION_UNIT, &unit);

#ifdef DEBUG
	printf("vips_image_resolution_from_exif: seen exif tags "
		   "xres = %g, yres = %g, unit = %d\n",
		xres, yres, unit);
#endif /*DEBUG*/

	switch (unit) {
	case 1:
		/* No units, instead xres / yres gives the pixel aspect ratio.
		 */
		break;

	case 2:
		/* In inches.
		 */
		xres /= 25.4;
		yres /= 25.4;
		vips_image_set_string(image,
			VIPS_META_RESOLUTION_UNIT, "in");
		break;

	case 3:
		/* In cm.
		 */
		xres /= 10.0;
		yres /= 10.0;
		vips_image_set_string(image,
			VIPS_META_RESOLUTION_UNIT, "cm");
		break;

	default:
		g_warning("%s", _("unknown EXIF resolution unit"));
		return -1;
	}

#ifdef DEBUG
	printf("vips_image_resolution_from_exif: "
		   "seen exif resolution %g, %g p/mm\n",
		xres, yres);
#endif /*DEBUG*/

	/* Don't allow negative resolution.
	 */
	image->Xres = VIPS_MAX(0, xres);
	image->Yres = VIPS_MAX(0, yres);

	return 0;
}

/* Need to fwd ref this.
 */
static int
vips_exif_resolution_from_image(ExifData *ed, VipsImage *image);

/* Scan the exif block on the image, if any, and make a set of vips metadata
 * tags for what we find.
 */
int
vips__exif_parse(VipsImage *image)
{
	const void *data;
	size_t size;
	ExifData *ed;
	VipsExifParams params;
	const char *str;

	if (!vips_image_get_typeof(image, VIPS_META_EXIF_NAME))
		return 0;
	if (vips_image_get_blob(image, VIPS_META_EXIF_NAME, &data, &size))
		return -1;
	if (!(ed = vips_exif_load_data_without_fix(data, size)))
		return -1;

#ifdef DEBUG_VERBOSE
	show_tags(ed);
	show_values(ed);
#endif /*DEBUG_VERBOSE*/

	/* Look for resolution fields and use them to set the VIPS xres/yres
	 * fields.
	 *
	 * If the fields are missing, set them from the image, which will have
	 * previously had them set from something like JFIF.
	 */
	if (vips_image_resolution_from_exif(image, ed) &&
		vips_exif_resolution_from_image(ed, image)) {
		exif_data_free(ed);
		return -1;
	}

	/* Make sure all required fields are there before we attach the vips
	 * metadata.
	 */
	exif_data_fix(ed);

	/* Attach informational fields for what we find.
	 */
	params.image = image;
	params.ed = ed;
	exif_data_foreach_content(ed,
		(ExifDataForeachContentFunc) vips_exif_get_content, &params);

	vips_image_set_blob_copy(image,
		"jpeg-thumbnail-data", ed->data, ed->size);

	exif_data_free(ed);

	/* Orientation handling. ifd0 has the Orientation tag for the main
	 * image.
	 */
	if (vips_image_get_typeof(image, "exif-ifd0-Orientation") != 0 &&
		!vips_image_get_string(image,
			"exif-ifd0-Orientation", &str)) {
		int orientation;

		orientation = atoi(str);
		if (orientation < 1 ||
			orientation > 8)
			orientation = 1;
		vips_image_set_int(image, VIPS_META_ORIENTATION, orientation);
	}

	return 0;
}

static void
vips_exif_set_int(ExifData *ed,
	ExifEntry *entry, unsigned long component, void *data)
{
	int value = *((int *) data);

	ExifByteOrder bo;
	size_t sizeof_component;
	size_t offset = component;

	if (entry->components <= component) {
		VIPS_DEBUG_MSG("vips_exif_set_int: too few components\n");
		return;
	}

	/* Wait until after the component check to make sure we can't get /0.
	 */
	bo = exif_data_get_byte_order(ed);
	sizeof_component = entry->size / entry->components;
	offset = component * sizeof_component;

	VIPS_DEBUG_MSG("vips_exif_set_int: %s = %d\n",
		vips_exif_entry_get_name(entry), value);

	if (entry->format == EXIF_FORMAT_SHORT)
		exif_set_short(entry->data + offset, bo, value);
	else if (entry->format == EXIF_FORMAT_SSHORT)
		exif_set_sshort(entry->data + offset, bo, value);
	else if (entry->format == EXIF_FORMAT_LONG)
		exif_set_long(entry->data + offset, bo, value);
	else if (entry->format == EXIF_FORMAT_SLONG)
		exif_set_slong(entry->data + offset, bo, value);
}

static void
vips_exif_double_to_rational(double value, ExifRational *rv)
{
	/* We will usually set factors of 10, so use 1000 as the denominator
	 * and it'll probably be OK.
	 */
	rv->numerator = value * 1000;
	rv->denominator = 1000;
}

static void
vips_exif_double_to_srational(double value, ExifSRational *srv)
{
	/* We will usually set factors of 10, so use 1000 as the denominator
	 * and it'll probably be OK.
	 */
	srv->numerator = value * 1000;
	srv->denominator = 1000;
}

/* Parse a char * into an ExifRational. We allow floats as well.
 */
static void
vips_exif_parse_rational(const char *str, ExifRational *rv)
{
	if (sscanf(str, " %u / %u ", &rv->numerator, &rv->denominator) == 2)
		return;
	vips_exif_double_to_rational(g_ascii_strtod(str, NULL), rv);
}

/* Parse a char * into an ExifSRational. We allow floats as well.
 */
static void
vips_exif_parse_srational(const char *str, ExifSRational *srv)
{
	if (sscanf(str, " %d / %d ",
			&srv->numerator, &srv->denominator) == 2)
		return;
	vips_exif_double_to_srational(g_ascii_strtod(str, NULL), srv);
}

/* Does both signed and unsigned rationals from a char *.
 */
static void
vips_exif_set_rational(ExifData *ed,
	ExifEntry *entry, unsigned long component, void *data)
{
	char *value = (char *) data;

	ExifByteOrder bo;
	size_t sizeof_component;
	size_t offset;

	if (entry->components <= component) {
		VIPS_DEBUG_MSG("vips_exif_set_rational: too few components\n");
		return;
	}

	/* Wait until after the component check to make sure we can't get /0.
	 */
	bo = exif_data_get_byte_order(ed);
	sizeof_component = entry->size / entry->components;
	offset = component * sizeof_component;

	VIPS_DEBUG_MSG("vips_exif_set_rational: %s = \"%s\"\n",
		vips_exif_entry_get_name(entry), value);

	if (entry->format == EXIF_FORMAT_RATIONAL) {
		ExifRational rv;

		vips_exif_parse_rational(value, &rv);

		VIPS_DEBUG_MSG("vips_exif_set_rational: %u / %u\n",
			rv.numerator,
			rv.denominator);

		exif_set_rational(entry->data + offset, bo, rv);
	}
	else if (entry->format == EXIF_FORMAT_SRATIONAL) {
		ExifSRational srv;

		vips_exif_parse_srational(value, &srv);

		VIPS_DEBUG_MSG("vips_exif_set_rational: %d / %d\n",
			srv.numerator, srv.denominator);

		exif_set_srational(entry->data + offset, bo, srv);
	}
}

/* Does both signed and unsigned rationals from a double*.
 *
 * Don't change the exit entry if the value currently there is a good
 * approximation of the double we are trying to set.
 */
static void
vips_exif_set_double(ExifData *ed,
	ExifEntry *entry, unsigned long component, void *data)
{
	double value = *((double *) data);

	ExifByteOrder bo;
	size_t sizeof_component;
	size_t offset;
	double old_value;

	if (entry->components <= component) {
		VIPS_DEBUG_MSG("vips_exif_set_double: too few components\n");
		return;
	}

	/* Wait until after the component check to make sure we can't get /0.
	 */
	bo = exif_data_get_byte_order(ed);
	sizeof_component = entry->size / entry->components;
	offset = component * sizeof_component;

	VIPS_DEBUG_MSG("vips_exif_set_double: %s = %g\n",
		vips_exif_entry_get_name(entry), value);

	if (entry->format == EXIF_FORMAT_RATIONAL) {
		ExifRational rv;

		rv = exif_get_rational(entry->data + offset, bo);
		if (rv.denominator == 0)
			old_value = 0;
		else
			old_value = (double) rv.numerator / rv.denominator;

		if (VIPS_FABS(old_value - value) > 0.0001) {
			vips_exif_double_to_rational(value, &rv);

			VIPS_DEBUG_MSG("vips_exif_set_double: %u / %u\n",
				rv.numerator,
				rv.denominator);

			exif_set_rational(entry->data + offset, bo, rv);
		}
	}
	else if (entry->format == EXIF_FORMAT_SRATIONAL) {
		ExifSRational srv;

		srv = exif_get_srational(entry->data + offset, bo);
		if (srv.denominator == 0)
			old_value = 0;
		else
			old_value = (double) srv.numerator / srv.denominator;

		if (VIPS_FABS(old_value - value) > 0.0001) {
			vips_exif_double_to_srational(value, &srv);

			VIPS_DEBUG_MSG("vips_exif_set_double: %d / %d\n",
				srv.numerator, srv.denominator);

			exif_set_srational(entry->data + offset, bo, srv);
		}
	}
}

typedef void (*write_fn)(ExifData *ed,
	ExifEntry *entry, unsigned long component, void *data);

/* String-valued tags need special treatment, sadly.
 *
 * Strings are written in three ways:
 *
 * 1. As ASCII, but with an 8-byte preamble giving the encoding (it's always
 * ASCII though) and the format undefined.
 * 2. As plain ASCII, with the format giving the encoding.
 * 3. As UTF16 in the MS tags.
 */

static gboolean
tag_is_encoding(ExifTag tag)
{
	return tag == EXIF_TAG_USER_COMMENT;
}

static gboolean
tag_is_ascii(ExifTag tag)
{
	return tag == EXIF_TAG_MAKE ||
		tag == EXIF_TAG_MODEL ||
		tag == EXIF_TAG_IMAGE_DESCRIPTION ||
		tag == EXIF_TAG_ARTIST ||
		tag == EXIF_TAG_SOFTWARE ||
		tag == EXIF_TAG_COPYRIGHT ||
		tag == EXIF_TAG_DATE_TIME ||
		tag == EXIF_TAG_DATE_TIME_ORIGINAL ||
		tag == EXIF_TAG_DATE_TIME_DIGITIZED ||
		tag == EXIF_TAG_SUB_SEC_TIME ||
		tag == EXIF_TAG_SUB_SEC_TIME_ORIGINAL ||
		tag == EXIF_TAG_SUB_SEC_TIME_DIGITIZED
#ifdef HAVE_EXIF_0_6_22
		|| tag == EXIF_TAG_CAMERA_OWNER_NAME ||
		tag == EXIF_TAG_BODY_SERIAL_NUMBER ||
		tag == EXIF_TAG_LENS_MAKE ||
		tag == EXIF_TAG_LENS_MODEL ||
		tag == EXIF_TAG_LENS_SERIAL_NUMBER
#endif
#ifdef HAVE_EXIF_0_6_23
		|| tag == EXIF_TAG_OFFSET_TIME ||
		tag == EXIF_TAG_OFFSET_TIME_ORIGINAL ||
		tag == EXIF_TAG_OFFSET_TIME_DIGITIZED ||
		tag == EXIF_TAG_GPS_LATITUDE_REF ||
		tag == EXIF_TAG_GPS_LONGITUDE_REF ||
		tag == EXIF_TAG_GPS_SATELLITES ||
		tag == EXIF_TAG_GPS_STATUS ||
		tag == EXIF_TAG_GPS_MEASURE_MODE ||
		tag == EXIF_TAG_GPS_SPEED_REF ||
		tag == EXIF_TAG_GPS_TRACK_REF ||
		tag == EXIF_TAG_GPS_IMG_DIRECTION_REF ||
		tag == EXIF_TAG_GPS_MAP_DATUM ||
		tag == EXIF_TAG_GPS_DEST_LATITUDE_REF ||
		tag == EXIF_TAG_GPS_DEST_LONGITUDE_REF ||
		tag == EXIF_TAG_GPS_DEST_BEARING_REF ||
		tag == EXIF_TAG_GPS_DEST_DISTANCE_REF ||
		tag == EXIF_TAG_GPS_DATE_STAMP
#endif
		;
}

static gboolean
tag_is_utf16(ExifTag tag)
{
	return tag == EXIF_TAG_XP_TITLE ||
		tag == EXIF_TAG_XP_COMMENT ||
		tag == EXIF_TAG_XP_AUTHOR ||
		tag == EXIF_TAG_XP_KEYWORDS ||
		tag == EXIF_TAG_XP_SUBJECT;
}

/* Set a libexif-formatted string entry.
 */
static void
vips_exif_alloc_string(ExifEntry *entry, unsigned long components)
{
	ExifMem *mem;

	g_assert(!entry->data);

	/* The string in the entry must be allocated with the same allocator
	 * that was used to allocate the entry itself. We can't do this
	 * because the allocator is private :( so we must assume the entry was
	 * created with the default one.
	 */
	mem = exif_mem_new_default();

	/* EXIF_FORMAT_UNDEFINED is correct for EXIF_TAG_USER_COMMENT, our
	 * caller should change this if it wishes.
	 */
	entry->data = exif_mem_alloc(mem, components);
	entry->size = components;
	entry->components = components;
	entry->format = EXIF_FORMAT_UNDEFINED;

	VIPS_FREEF(exif_mem_unref, mem);
}

/* The final " (xx, yy, zz, kk)" part of the string (if present) was
 * added by us in _to_s(), we must remove it before setting the string
 * back again.
 *
 * It may not be there if the user has changed the string.
 */
static char *
drop_tail(const char *data)
{
	char *str;
	char *p;

	str = g_strdup(data);

	p = str + strlen(str);
	if (p > str &&
		*g_utf8_prev_char(p) == ')' &&
		(p = g_utf8_strrchr(str, -1, (gunichar) '(')) &&
		p > str &&
		*(p = g_utf8_prev_char(p)) == ' ')
		*p = '\0';

	return str;
}

/* special header required for EXIF_TAG_USER_COMMENT.
 */
#define ASCII_COMMENT "ASCII\0\0\0"

/* Write a libvips NULL-terminated utf-8 string into a entry tagged with a
 * encoding. UserComment is like this, for example.
 */
static void
vips_exif_set_string_encoding(ExifData *ed,
	ExifEntry *entry, unsigned long component, const char *data)
{
	char *str;
	char *ascii;
	int len;

	str = drop_tail(data);

	/* libexif can only really save ASCII to things like UserComment.
	 */
	ascii = g_str_to_ascii(str, NULL);
	g_free(str);
	str = ascii;

	/* libexif comment strings are not NULL-terminated, and have an
	 * encoding tag (always ASCII) in the first 8 bytes.
	 */
	len = strlen(str);
	vips_exif_alloc_string(entry, sizeof(ASCII_COMMENT) - 1 + len);
	memcpy(entry->data, ASCII_COMMENT, sizeof(ASCII_COMMENT) - 1);
	memcpy(entry->data + sizeof(ASCII_COMMENT) - 1, str, len);

	g_free(str);
}

/* Write a libvips NULL-terminated utf-8 string into an ASCII entry. Tags like
 * ImageDescription work like this.
 */
static void
vips_exif_set_string_ascii(ExifData *ed,
	ExifEntry *entry, unsigned long component, const char *data)
{
	char *str;
	char *ascii;
	int len;

	str = drop_tail(data);

	/* libexif can only really save ASCII to things like UserComment.
	 */
	ascii = g_str_to_ascii(str, NULL);
	g_free(str);
	str = ascii;

	/* ASCII strings are NULL-terminated.
	 */
	len = strlen(str);
	vips_exif_alloc_string(entry, len + 1);
	memcpy(entry->data, str, len + 1);
	entry->format = EXIF_FORMAT_ASCII;

	g_free(str);
}

/* Write a libvips NULL-terminated utf-8 string into a utf16 entry.
 */
static void
vips_exif_set_string_utf16(ExifData *ed,
	ExifEntry *entry, unsigned long component, const char *data)
{
	char *str;
	gunichar2 *utf16;
	glong len;

	str = drop_tail(data);

	utf16 = g_utf8_to_utf16(str, -1, NULL, &len, NULL);

	/* libexif utf16 strings are NULL-terminated.
	 */
	vips_exif_alloc_string(entry, (len + 1) * 2);
	memcpy(entry->data, utf16, (len + 1) * 2);
	entry->format = EXIF_FORMAT_BYTE;

	g_free(utf16);
	g_free(str);
}

/* Write a tag. Update what's there, or make a new one.
 */
static void
vips_exif_set_tag(ExifData *ed, int ifd, ExifTag tag, write_fn fn, void *data)
{
	ExifEntry *entry;

	if ((entry = exif_content_get_entry(ed->ifd[ifd], tag))) {
		fn(ed, entry, 0, data);
	}
	else {
		entry = exif_entry_new();

		/* tag must be set before calling exif_content_add_entry.
		 */
		entry->tag = tag;
		exif_content_add_entry(ed->ifd[ifd], entry);
		exif_entry_unref(entry);

		/* libexif makes us have a special path for string-valued
		 * fields :(
		 */
		if (tag_is_encoding(tag))
			vips_exif_set_string_encoding(ed, entry, 0, data);
		else if (tag_is_ascii(tag))
			vips_exif_set_string_ascii(ed, entry, 0, data);
		else if (tag_is_utf16(tag))
			vips_exif_set_string_utf16(ed, entry, 0, data);
		else {
			exif_entry_initialize(entry, tag);
			fn(ed, entry, 0, data);
		}
	}
}

/* Set the EXIF resolution from the vips xres/yres tags.
 */
static int
vips_exif_resolution_from_image(ExifData *ed, VipsImage *image)
{
	double xres, yres;
	const char *p;
	int unit;

	VIPS_DEBUG_MSG("vips_exif_resolution_from_image: vips res of %g, %g\n",
		image->Xres, image->Yres);

	/* Default to inches, more progs support it.
	 */
	unit = 2;
	if (vips_image_get_typeof(image, VIPS_META_RESOLUTION_UNIT) &&
		!vips_image_get_string(image,
			VIPS_META_RESOLUTION_UNIT, &p)) {
		if (vips_isprefix("cm", p))
			unit = 3;
		else if (vips_isprefix("none", p))
			unit = 1;
	}

	switch (unit) {
	case 1:
		xres = image->Xres;
		yres = image->Yres;
		break;

	case 2:
		xres = image->Xres * 25.4;
		yres = image->Yres * 25.4;
		break;

	case 3:
		xres = image->Xres * 10.0;
		yres = image->Yres * 10.0;
		break;

	default:
		g_warning("%s", _("unknown EXIF resolution unit"));
		return 0;
	}

	/* Main image xres/yres/unit are in ifd0. ifd1 has the thumbnail
	 * xres/yres/unit.
	 */
	vips_exif_set_tag(ed, 0, EXIF_TAG_X_RESOLUTION,
		vips_exif_set_double, (void *) &xres);
	vips_exif_set_tag(ed, 0, EXIF_TAG_Y_RESOLUTION,
		vips_exif_set_double, (void *) &yres);
	vips_exif_set_tag(ed, 0, EXIF_TAG_RESOLUTION_UNIT,
		vips_exif_set_int, (void *) &unit);

	return 0;
}

/* Exif also tracks image dimensions.
 */
static int
vips_exif_set_dimensions(ExifData *ed, VipsImage *im)
{
	VIPS_DEBUG_MSG("vips_exif_set_dimensions: vips size of %d, %d\n",
		im->Xsize, im->Ysize);

	vips_exif_set_tag(ed, 2, EXIF_TAG_PIXEL_X_DIMENSION,
		vips_exif_set_int, (void *) &im->Xsize);
	vips_exif_set_tag(ed, 2, EXIF_TAG_PIXEL_Y_DIMENSION,
		vips_exif_set_int, (void *) &im->Ysize);

	return 0;
}

/* And orientation.
 */
static int
vips_exif_set_orientation(ExifData *ed, VipsImage *im)
{
	int orientation;

	/* We set the tag, even if it's been deleted, since it's a required
	 * field.
	 */
	if (!vips_image_get_typeof(im, VIPS_META_ORIENTATION) ||
		vips_image_get_int(im, VIPS_META_ORIENTATION, &orientation))
		orientation = 1;

	VIPS_DEBUG_MSG("set_exif_orientation: %d\n", orientation);

	vips_exif_set_tag(ed, 0, EXIF_TAG_ORIENTATION,
		vips_exif_set_int, (void *) &orientation);

	return 0;
}

/* And thumbnail.
 */
static int
vips_exif_set_thumbnail(ExifData *ed, VipsImage *im)
{
	/* Delete any old thumbnail data. We should use the exif free func,
	 * but the memory allocator is not exposed by libexif! Hopefully they
	 * are just using free().
	 *
	 * exif.c makes this assumption too when it tries to update a
	 * thumbnail.
	 */
	if (ed->data) {
		free(ed->data);
		ed->data = NULL;
	}
	ed->size = 0;

	/* Update EXIF thumbnail from metadata, if any.
	 */
	if (vips_image_get_typeof(im, "jpeg-thumbnail-data")) {
		const void *data;
		size_t size;

		if (vips_image_get_blob(im, "jpeg-thumbnail-data",
				&data, &size))
			return -1;

		/* Again, we should use the exif allocator attached to this
		 * entry, but it is not exposed!
		 */
		if (size > 0 &&
			data) {
			ed->data = malloc(size);
			memcpy(ed->data, data, size);
			ed->size = size;
		}
	}

	return 0;
}

/* Skip any spaces.
 */
static const char *
skip_space(const char *p)
{
	while (p && *p == ' ')
		p += 1;

	return p;
}

/* Skip to the end of this non-space sequence.
 */
static const char *
skip_nonspace(const char *p)
{
	while (p && *p && *p != ' ')
		p += 1;

	return p;
}

/* See also vips_exif_to_s() ... keep in sync. Only the numeric types are
 * handled here, since they can be updated. For string types, we have to
 * destroy and recreate, see above.
 */
static void
vips_exif_from_s(ExifData *ed, ExifEntry *entry, const char *value)
{
	unsigned long i;
	const char *p;
	int v;

	if (entry->format == EXIF_FORMAT_SHORT ||
		entry->format == EXIF_FORMAT_SSHORT ||
		entry->format == EXIF_FORMAT_LONG ||
		entry->format == EXIF_FORMAT_SLONG) {
		if (entry->components >= 10)
			return;

		p = value;
		for (i = 0; i < entry->components; i++) {
			if (!(p = skip_space(p)))
				break;

			v = atof(p);
			vips_exif_set_int(ed, entry, i, &v);

			p = skip_nonspace(p);
		}
	}
	else if (entry->format == EXIF_FORMAT_RATIONAL ||
		entry->format == EXIF_FORMAT_SRATIONAL) {
		if (entry->components >= 10)
			return;

		p = value;
		for (i = 0; i < entry->components; i++) {
			if (!(p = skip_space(p)))
				break;

			vips_exif_set_rational(ed, entry, i, (void *) p);

			p = skip_nonspace(p);
		}
	}
}

static void
vips_exif_set_entry(ExifData *ed, ExifEntry *entry,
	unsigned long component, void *data)
{
	const char *string = (const char *) data;

	vips_exif_from_s(ed, entry, string);
}

static void *
vips_exif_image_field(VipsImage *image,
	const char *field, GValue *value, void *data)
{
	ExifData *ed = (ExifData *) data;

	const char *string;
	int ifd;
	const char *p;
	ExifTag tag;

	if (!vips_isprefix("exif-ifd", field))
		return NULL;

	/* value must be a string.
	 */
	if (vips_image_get_string(image, field, &string)) {
		g_warning(_("bad exif meta \"%s\""), field);
		return NULL;
	}

	p = field + strlen("exif-ifd");
	ifd = atoi(p);

	for (; g_ascii_isdigit(*p); p++)
		;
	if (*p != '-') {
		g_warning(_("bad exif meta \"%s\""), field);
		return NULL;
	}

	/* GPSVersionID is tag 0 (the error return) so we have to
	 * test the name too.
	 */
	if (!(tag = exif_tag_from_name(p + 1)) &&
		strcmp(p + 1, "GPSVersionID") != 0) {
		g_warning(_("bad exif meta \"%s\""), field);
		return NULL;
	}

	vips_exif_set_tag(ed, ifd, tag, vips_exif_set_entry, (void *) string);

	return NULL;
}

typedef struct _VipsExifRemove {
	VipsImage *image;
	ExifData *ed;
	ExifContent *content;
	GSList *to_remove;
} VipsExifRemove;

static void
vips_exif_exif_entry(ExifEntry *entry, VipsExifRemove *ve)
{
	const char *tag_name;
	char vips_name_txt[256];
	VipsBuf vips_name_buf = VIPS_BUF_STATIC(vips_name_txt);

	const char *vips_name;
	const char *vips_value;

	if (!(tag_name = vips_exif_entry_get_name(entry)))
		return;

	vips_buf_appendf(&vips_name_buf, "exif-ifd%d-%s",
		exif_entry_get_ifd(entry), tag_name);
	vips_name = vips_buf_all(&vips_name_buf);

	/* Is there a image metadata item for this tag?
	 */
	vips_value = NULL;
	if (vips_image_get_typeof(ve->image, vips_name)) {
		/* No easy way to return an error code from here, sadly.
		 */
		if (vips_image_get_string(ve->image, vips_name, &vips_value))
			g_warning(_("bad exif meta \"%s\""), vips_name);
	}

	/* Does this field exist on the image? If not, schedule it for
	 * removal.
	 */
	if (!vips_value)
		ve->to_remove = g_slist_prepend(ve->to_remove, entry);

	/* Orientation is really set from the vips
	 * VIPS_META_ORIENTATION tag. If that's been deleted, we must delete
	 * any matching EXIF tags too.
	 */
	if (strcmp(tag_name, "Orientation") == 0 &&
		vips_value)
		ve->to_remove = g_slist_prepend(ve->to_remove, entry);

	/* If this is a string tag with a new value, we must also remove it
	 * ready for recreation, see the comment below.
	 */
	if (vips_value &&
		(tag_is_encoding(entry->tag) ||
			tag_is_ascii(entry->tag) ||
			tag_is_utf16(entry->tag))) {
		VipsDbuf value = { 0 };

		/* Render the original exif-data value to a string and see
		 * if the user has changed it. If they have, remove it ready
		 * for re-adding.
		 *
		 * Leaving it there prevents it being recreated.
		 */
		vips_exif_to_s(ve->ed, entry, &value);
		if (strcmp((char *) vips_dbuf_string(&value, NULL), vips_value) != 0)
			ve->to_remove = g_slist_prepend(ve->to_remove, entry);
		vips_dbuf_destroy(&value);
	}
}

static void *
vips_exif_exif_remove(ExifEntry *entry, VipsExifRemove *ve, void *b)
{
#ifdef DEBUG
	{
		const char *tag_name;
		char vips_name_txt[256];
		VipsBuf vips_name = VIPS_BUF_STATIC(vips_name_txt);

		tag_name = vips_exif_entry_get_name(entry);
		vips_buf_appendf(&vips_name, "exif-ifd%d-%s",
			exif_entry_get_ifd(entry), tag_name);

		printf("vips_exif_exif_remove: %s\n", vips_buf_all(&vips_name));
	}
#endif /*DEBUG*/

	exif_content_remove_entry(ve->content, entry);

	return NULL;
}

static void
vips_exif_exif_content(ExifContent *content, VipsExifRemove *ve)
{
	ve->content = content;
	ve->to_remove = NULL;
	exif_content_foreach_entry(content,
		(ExifContentForeachEntryFunc) vips_exif_exif_entry, ve);
	vips_slist_map2(ve->to_remove,
		(VipsSListMap2Fn) vips_exif_exif_remove, ve, NULL);
	VIPS_FREEF(g_slist_free, ve->to_remove);
}

static void
vips_exif_update(ExifData *ed, VipsImage *image)
{
	VipsExifRemove ve;

	VIPS_DEBUG_MSG("vips_exif_update: \n");

	/* If this exif came from the image (rather than being an exif block we
	 * have made afresh), then any fields which are in the block but not on
	 * the image must have been deliberately removed. Remove them from the
	 * block as well.
	 *
	 * Any string-valued fields (eg. comment etc.) which exist as libvips
	 * metadata tags with changed  whose values have changed must also be
	 * removed.
	 *
	 * libexif does not allow you to change string lengths (you must make
	 * new tags) so we have to remove ready to re-add.
	 */
	if (vips_image_get_typeof(image, VIPS_META_EXIF_NAME)) {
		ve.image = image;
		ve.ed = ed;
		exif_data_foreach_content(ed,
			(ExifDataForeachContentFunc) vips_exif_exif_content,
			&ve);
	}

	/* Walk the image and add any exif- that's set in image metadata.
	 */
	vips_image_map(image, vips_exif_image_field, ed);
}

/* Examine the metadata tags on the image and update the EXIF block.
 */
int
vips__exif_update(VipsImage *image)
{
	unsigned char *data;
	size_t length;
	unsigned int idl;
	ExifData *ed;

	/* Either parse from the embedded EXIF, or if there's none, make
	 * some fresh EXIF we can write the resolution to.
	 */
	if (vips_image_get_typeof(image, VIPS_META_EXIF_NAME)) {
		if (vips_image_get_blob(image, VIPS_META_EXIF_NAME,
				(void *) &data, &length))
			return -1;

		if (!(ed = vips_exif_load_data_without_fix(data, length)))
			return -1;
	}
	else {
		ed = exif_data_new();

		exif_data_set_option(ed,
			EXIF_DATA_OPTION_FOLLOW_SPECIFICATION);
		exif_data_set_data_type(ed, EXIF_DATA_TYPE_COMPRESSED);
		exif_data_set_byte_order(ed, EXIF_BYTE_ORDER_INTEL);
	}

	/* Make sure all required fields are there before we attach the vips
	 * metadata.
	 */
	exif_data_fix(ed);

	/* Update EXIF tags from the image metadata.
	 */
	vips_exif_update(ed, image);

	/* Update EXIF resolution from the vips image header.
	 */
	if (vips_exif_resolution_from_image(ed, image)) {
		exif_data_free(ed);
		return -1;
	}

	/* Update EXIF image dimensions from the vips image header.
	 */
	if (vips_exif_set_dimensions(ed, image)) {
		exif_data_free(ed);
		return -1;
	}

	/* Update EXIF orientation from the vips image header.
	 */
	if (vips_exif_set_orientation(ed, image)) {
		exif_data_free(ed);
		return -1;
	}

	/* Update the thumbnail.
	 */
	if (vips_exif_set_thumbnail(ed, image)) {
		exif_data_free(ed);
		return -1;
	}

	/* Reserialise and write. exif_data_save_data() returns an int for some
	 * reason.
	 */
	exif_data_save_data(ed, &data, &idl);
	if (!idl) {
		vips_error("exif", "%s", _("error saving EXIF"));
		exif_data_free(ed);
		return -1;
	}
	length = idl;

#ifdef DEBUG
	printf("vips__exif_update: generated %zd bytes of EXIF\n", length);
#endif /*DEBUG*/

	vips_image_set_blob(image, VIPS_META_EXIF_NAME,
		(VipsCallbackFn) vips_area_free_cb, data, length);

	exif_data_free(ed);

	return 0;
}

#else /*!HAVE_EXIF*/

int
vips__exif_parse(VipsImage *image)
{
	return 0;
}

int
vips__exif_update(VipsImage *image)
{
	return 0;
}

#endif /*!HAVE_EXIF*/
